/*
   程序名称：bkipc.c
   程序内容：与共享内存、文件锁、信号量有关的函数
   程序作者：姚杰
   完成日期：2012 年 07 月 01 日
*/

#include "bkbase.h"
#include "bkipc.h"

#include <sys/sem.h>
#include <sys/shm.h>

#ifdef __cplusplus
extern "C" {
#endif

static int    AlarmFlag;

static void  CatchSigAlarm(int sig);

static void CatchSigAlarm(int sig)
{
   AlarmFlag = 1;
}

int BkCreateShm(key_t key, size_t size, int perms)
{
   BK_ASSERT( key != -1 );

   if( size < 256 )
      size = 256;

   return shmget(key, size, IPC_CREAT | IPC_EXCL | perms);
}

int BkRemoveShm(key_t key)
{
   int ShmId;

   BK_ASSERT( key != -1 );

   ShmId = shmget(key, 0, 0);
   return shmctl(ShmId, IPC_RMID, NULL);
}

char *BkAttachShm(key_t key)
{
   int ShmId;
   char *p;

   BK_ASSERT( key != -1 );

   ShmId = shmget(key, 0, 0);
   p = (char*)shmat(ShmId, NULL, 0);

   if( p == (char*) -1 )
      return NULL;
   else
      return p;
}

int BkDetachShm(char *p)
{
   BK_ASSERT( p != NULL );

   return shmdt(p);
}

int BkCreateSem(key_t key, int semval, int perms)
{
   int SemId;
   union {
      int val;
      struct semid_ds *buf;
      ushort *array;
   } semun;

   BK_ASSERT( key != -1 );

   SemId = semget(key, 1, IPC_CREAT | IPC_EXCL | perms);
   semun.val = semval;
   return semctl(SemId, 0, SETVAL, semun);
}

int BkRemoveSem(key_t key)
{
   int SemId;

   BK_ASSERT( key != -1 );

   SemId = semget(key, 0, 0);

   if( SemId == -1 )
      return -1;

   return semctl(SemId, 0, IPC_RMID, 0);
}

int BkGetSemNum(key_t key)
{
   int SemId;

   if( key == -1 )
      return -1;

   SemId = semget(key, 0, 0);

   if( SemId == -1 )
      return -1;

   return semctl(SemId, 0, GETVAL, 0);
}

int BkDijkstra_P(key_t key)
{
   int SemId, r;
   struct sembuf buf;

   BK_ASSERT( key != -1 );

   SemId = semget(key, 0, 0);

   if( SemId == -1 ){
      BkWriteErrorLog("BkDijkstra_P : semget fail, errno = %d", errno);
      return -1;
   }

   buf.sem_op  = -1;
   buf.sem_flg = SEM_UNDO;
   buf.sem_num = 0;

   for(;;){
      r = semop(SemId, &buf, 1);
      if( r == -1 && errno == EINTR )
         continue;
      break;
   }

   if( r == -1 )
      BkWriteErrorLog("BkDijkstra_P : semop fail, errno = %d", errno);

   return r;
}

int BkDijkstra_V(key_t key)
{
   int SemId, r;
   struct sembuf buf;

   BK_ASSERT( key != -1 );

   SemId = semget(key, 0, 0);

   if( SemId == -1 ){
      BkWriteErrorLog("BkDijkstra_V : semget, errno = %d", errno);
      return -1;
   }

   buf.sem_op  = 1;
   buf.sem_flg = SEM_UNDO;
   buf.sem_num = 0;

   for(;;){
      r = semop(SemId, &buf, 1);
      if( r == -1 && errno == EINTR )
         continue;
      break;
   }

   if( r == -1 )
      BkWriteErrorLog("BkDijkstra_P : semop fail, errno = %d", errno);

   return r;
}

int BkOpenLockFile(const char *FileName)
{
   return open(FileName, O_CREAT | O_RDWR, 0660);
}

int BkReadLock(int fd, int offset, int len, int TimeOut)
{
   int r, err;
   struct flock lock;
   void (*OldAction)(int);

   lock.l_type = F_RDLCK;
   lock.l_start = offset;
   lock.l_whence = SEEK_SET;
   lock.l_len = len;

   if( TimeOut == 0 ){
      r = fcntl(fd, F_SETLK, &lock);
      err = errno;
   }
   else if( TimeOut == BK_TIME_INFINITE ){
      r = fcntl(fd, F_SETLKW, &lock);
      err = errno;
   }
   else{
      OldAction = BkSignal(SIGALRM, CatchSigAlarm, 1);
      AlarmFlag = 0;
      alarm(TimeOut);
      r = fcntl(fd, F_SETLKW, &lock);
      err = errno;
      alarm(0);
      BkSignal(SIGALRM, OldAction, 0);
   }

   if( r == 0 )
      return 0;

   if( TimeOut != 0 && TimeOut != BK_TIME_INFINITE && AlarmFlag == 1 )
      return BK_ETIMEOUT;

   if( err == EINTR )
      return BK_EINTR;

   if( err == EDEADLK )
      return BK_EDEADLK;

   return BK_EOTHER;
}

int BkWriteLock(int fd, int offset, int len, int TimeOut)
{
   int r, err;
   struct flock lock;
   void (*OldAction)(int);

   lock.l_type = F_WRLCK;
   lock.l_start = offset;
   lock.l_whence = SEEK_SET;
   lock.l_len = len;

   if( TimeOut == 0 ){
      r = fcntl(fd, F_SETLK, &lock);
      err = errno;
   }
   else if( TimeOut == BK_TIME_INFINITE ){
      r = fcntl(fd, F_SETLKW, &lock);
      err = errno;
   }
   else{
      OldAction = BkSignal(SIGALRM, CatchSigAlarm, 1);
      AlarmFlag = 0;
      alarm(TimeOut);
      r = fcntl(fd, F_SETLKW, &lock);
      err = errno;
      alarm(0);
      BkSignal(SIGALRM, OldAction, 0);
   }

   if( r == 0 )
      return 0;

   if( TimeOut != 0 && TimeOut != BK_TIME_INFINITE && AlarmFlag == 1 )
      return BK_ETIMEOUT;

   if( err == EINTR )
      return BK_EINTR;

   if( err == EDEADLK )
      return BK_EDEADLK;

   return BK_EOTHER;
}

int BkUnlock(int fd, int offset, int len)
{
   struct flock lock;

   lock.l_type = F_UNLCK;
   lock.l_start = offset;
   lock.l_whence = SEEK_SET;
   lock.l_len = len;

   if( fcntl(fd, F_SETLK, &lock) == -1 )
      return BK_EOTHER;

   return 0;
}

#ifdef __cplusplus
}
#endif
